﻿using Base.FileService.Interface;
using WeDonekRpc.CacheClient.Interface;
using WeDonekRpc.Client.Attr;
using WeDonekRpc.Helper;
using WeDonekRpc.Helper.Interface;

namespace Base.FileService.lmpl
{
    [ClassLifetimeAttr(ClassLifetimeType.SingleInstance)]
    internal class UserFileAccredit : IUserFileAccredit
    {
        private struct _ExpireKey
        {
            public string[] Key;
            public DateTime Time;
        }
        private readonly IRedisStringController _Redis;
        private static readonly string _CacheKey = "FileAccredit_";
        private readonly IDelayDataSave<_ExpireKey> _queue;

        private readonly ILocalCacheController _Local;
        public UserFileAccredit ( IRedisStringController redis, ILocalCacheController local )
        {
            this._Local = local;
            this._Redis = redis;
            this._queue = new DelayDataSave<_ExpireKey>(this._SetExpire, 2, 5);
        }

        private void _SetExpire ( ref _ExpireKey[] datas )
        {
            List<_ExpireKey> fails = new List<_ExpireKey>();
            DateTime now = DateTime.Now;
            foreach ( _ExpireKey item in datas )
            {
                if ( item.Time > now )
                {
                    TimeSpan timeSpan = item.Time - now;
                    string[] keys = item.Key.FindAll(c =>
                    {
                        return this._Redis.SetExpire(c, item.Time - now) == false;
                    });
                    if ( !keys.IsNull() )
                    {
                        fails.Add(new _ExpireKey
                        {
                            Key = keys,
                            Time = item.Time
                        });
                    }
                }
                else if ( this._Redis.Remove(item.Key) == false )
                {
                    fails.Add(item);
                }
            }
            if ( fails.Count > 0 )
            {
                datas = fails.ToArray();
                throw new ErrorException("file.accredit.expire.set.fail");
            }
        }

        public bool Set ( string accreditId, long[] fileId, int overTime )
        {
            int time = HeartbeatTimeHelper.HeartbeatTime + overTime;
            if ( fileId.Length == 1 )
            {
                string key = _CacheKey + accreditId + "_" + fileId[0];
                DateTime t = DateTime.Now.AddSeconds(overTime);
                if ( this._Redis.Set<int>(key, time, t) )
                {
                    _ = this._Local.Set(key, time, t);
                    return true;
                }
                return false;
            }
            else
            {
                string key = _CacheKey + accreditId;
                string[] keys = fileId.ConvertAll(c => key + "_" + c);
                if ( this._Redis.Set(keys, time.ToString()) )
                {
                    DateTime t = DateTime.Now.AddSeconds(overTime);
                    keys.ForEach(c =>
                    {
                        _ = this._Local.Set(c, time, t);
                    });
                    this._queue.AddData(new _ExpireKey
                    {
                        Key = keys,
                        Time = t
                    });
                    return true;
                }
                return false;
            }
        }
        public bool CheckIsAccredit ( string accreditId, long fileId )
        {
            string key = _CacheKey + accreditId + "_" + fileId;
            if ( this._Local.TryGet(key, out int time) || this._Redis.TryGet(key, out time) )
            {
                return time >= HeartbeatTimeHelper.HeartbeatTime;
            }
            return false;
        }
    }
}
